#include "CPU.h"
#include "arch.h"
#include "gdb.h"
#include "base/MemoryRequest.h"
#include "base/Simulator.h"
#include "base/simapi.h"
#include "base/simtypes.h"
#include "base/syscalls.h"
#include "config.h"

NEW_DELETE_OPERATORS_CPP(Checkpoint,100);
NEW_DELETE_OPERATORS_CPP(CPU,100);

static uint partial_mask_hi[][4] = {
  { 0x00000000, 0x00000000, 0x00000000, 0x00000000,},
  { 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000,},
  { 0x0000FFFF, 0x00FFFF00, 0xFFFF0000, 0xFF000000,},
  { 0x00000000, 0x00000000, 0x00000000, 0x00000000,},
  { 0xFFFFFFFF, 0xFFFFFF00, 0xFFFF0000, 0xFF000000,},
  { 0x00000000, 0x00000000, 0x00000000, 0x00000000,},
  { 0x00000000, 0x00000000, 0x00000000, 0x00000000,},
  { 0x00000000, 0x00000000, 0x00000000, 0x00000000,},
  { 0xFFFFFFFF, 0xFFFFFF00, 0xFFFF0000, 0xFF000000,},
};

// This routine will get "returned" to when the processor 
// completes execution of mainX.
// We already have mechanisms in place to deal with the
// exit syscall, so this enables us to detect termination
// conditions in a single place, without checking on
// every cycle.

static volatile int reg_eax;
void NativeSafetyExitProc () {
  asm volatile("movl reg_eax, %%eax;\n" : : : "eax","memory");
  // austenmc: reg_eax has exit code in it
  exit(reg_eax);
  //  global_sim->stop(reg_eax);
}

CPU::CPU (Processor* P,
          Byte* stack) :
    P(P),
    stack_aligned_end(stack)
{
    curPE = NULL;
}

CPU::~CPU () {
}

void CPU::reset (VA startPC, VA returnPC, 
                 int argc, Reg arg0, Reg arg1, Reg arg2, Reg arg3) 
{
    CPU *saved_curPE = curPE;
    curPE = this;
    // based on LESS86/native/x86native.cpp NativeEntryPoint
    setFlags(0x3202);
    for (int i = 0; i < (X86_NUM_GPR + 1); i++) {
        InterpCtxt.GPR[i] = 0;
    }
    InterpCtxt.GPR[EBP] = originalBP;
    if (returnPC == 0) {
        returnPC = ADDR_UINT64((void*)&NativeSafetyExitProc);
    }
    setupStack(stack_aligned_end, 
               startPC,
               returnPC,
               argc, arg0, arg1, arg2, arg3);

#if CFG_FPU
    FINIT();
#if defined(WIN32) || defined(_WIN32)
    FpuSetControlWord(0x27F);
#endif

    // TODO: clean up this reset state/init
    // TODO: when SSE supported, clean up this flag
    // InterpCtxt.SSEscr = 0x1F80; // proper reset state
    InterpCtxt.SSEscr = 0x1F00;
#endif

    InterpCtxt.SegTable[CS].base = 0;
    InterpCtxt.SegTable[CS].limit = 0xFFFFFFFF;
    InterpCtxt.SegTable[DS].base = 0;
    InterpCtxt.SegTable[DS].limit = 0xFFFFFFFF;
    InterpCtxt.SegTable[ES].base = 0;
    InterpCtxt.SegTable[ES].limit = 0xFFFFFFFF;
    InterpCtxt.SegTable[SS].base = 0;
    InterpCtxt.SegTable[SS].limit = 0xFFFFFFFF;
#if CFG_NATIVE_PROCESS_FS_INTERCEPT
    InterpCtxt.SegTable[FS].base = SegInterceptBaseFS;
    InterpCtxt.SegTable[FS].limit = SegInterceptLimitFS;
    InterpCtxt.fsSegSelector = 0;
#else
    InterpCtxt.SegTable[FS].base = 0;
    InterpCtxt.SegTable[FS].limit = 0;
#endif
#if CFG_NATIVE_PROCESS_GS_INTERCEPT
    InterpCtxt.SegTable[GS].base = SegInterceptBaseGS;
    InterpCtxt.SegTable[GS].limit = SegInterceptLimitGS;
    InterpCtxt.gsSegSelector = 0;
#else
    InterpCtxt.SegTable[GS].base = 0;
    InterpCtxt.SegTable[GS].limit = 0;
#endif
    
    // based on LESS86/x86/interpreter/interp.cpp Interp
    InterpCtxt.fSavedCSFlagD = GetSegAttr(CS,SEG_FLAG_D);
    InterpCtxt.fSavedSSFlagB = GetSegAttr(SS,SEG_FLAG_B);

    curPE = saved_curPE;
}

void CPU::setupStack (Byte* stack,
                      Reg startAddress,
                      Reg returnAddress,
                      int argc,
                      Reg arg0,
                      Reg arg1,
                      Reg arg2,
                      Reg arg3)
{
    global_sim->getTM()->threadLog.d(
        "%d:%lld: [CPU] setupStack stack=%p start=%llx ends=%llx "
        "argc=%d arg1=%llx arg2=%llx arg3=%llx arg4=%llx\n",
        P->getID(), global_sim->getSimclock(),
        stack, startAddress, returnAddress, 
        argc, arg0, arg1, arg2, arg3);

    if (stack == NULL) {
        // reuse existing stack, skipping top and at least one
        // cacheline. since we invalidate the part of the stack we
        // right, we do not want to use a cacheline that contains
        // other useful data
        stack = (Byte*)(ADDR_UINT32(InterpCtxt.GPR[ESP]) - 
                        (WORD_SIZE + global_sim->cfg.blocksize));
    }

    // 1 for each argument + 1 for EBP and + 1 for return address
    int frameSizeWords = (argc) + 1 + 1;
    UINT32* stackPointer = ((UINT32*) stack) - frameSizeWords;

    // invalidate any parts of the frame we just set currently in cache.
    // happens when CPU is reused for later thread.
    if (global_sim->getMemory() != NULL) {
        // first time through this function, you dont even have caches...

        size_t frameSizeBytes = frameSizeWords * WORD_SIZE;
        Log->d("invaldating stack addr=%p-%p\n",
               stackPointer,
               stackPointer+frameSizeBytes);
        // raw flush make sure any in flight StoreRequests are done
        // before we invalidate cache. make sure we take care of all
        // these pending writes before we write our own data to the
        // stack
        global_sim->rawFlushCaches(               ADDR_UINT64(stackPointer), frameSizeBytes);
        global_sim->invalidateCaches(P, __func__, ADDR_UINT64(stackPointer), frameSizeBytes);
    }

    InterpCtxt.IP = startAddress;
    InterpCtxt.GPR[ESP] = (IVAL)(UINT32)stackPointer;
    *(stackPointer + frameSizeWords - 1) = InterpCtxt.GPR[EBP];
    switch (argc) {
      case 4: *(stackPointer+4) = (UINT32)arg3;
      case 3: *(stackPointer+3) = (UINT32)arg2;
      case 2: *(stackPointer+2) = (UINT32)arg1;
      case 1: *(stackPointer+1) = (UINT32)arg0;
    }
    *(stackPointer + 0) = (UINT32)returnAddress;
}

void CPU::printInstruction (Logger* log, 
                            SEVERITY severity, 
                            bool print_registers,
                            bool print_symbols)
{
    PrintInstruction(log,
                     severity,
                     this,
                     print_registers,
                     print_symbols);
}

CPU* curPE;

bool CPU::fetchDecodeExecuteCommitInstruction () {
    curPE = this;

    /* fetch */
    IVAL curIP = InterpCtxt.IP;

    // reset any possible side-effects of having prefixes
    tmp.prefixes = 0;
    tmp.fOpSize = tmp.fAddrSize = InterpCtxt.fSavedCSFlagD;
    tmp.segOverride = SEG_NONE;

    // reset softfloat for current processor 
    FpuSetControlWord(X86_CTXT(FPcontrol));

    /* decode */
    // process bytes in Table 1 until we reach the opcode
    UINT ofs = 1;
    UINT8 i0 = LOAD_VIRTUAL_BYTE_UNCACHED(curIP,CS,ACCESS_EXECUTE);
    UINT32 bits = x86Table1[i0].bits;
    PINSTRFUNC func = NULL;
    while (bits & X86_BITS_INITIAL) {
      switch (bits & X86_BITS_INITIAL_MASK) {
        case X86_BITS_TABLE2: {
          // Table 2 lookup
          UINT8 i1 = LOAD_VIRTUAL_BYTE_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
#if CFG_SSE || CFG_SSE2 || CFG_SSE3
          UINT idx = i1 | ((prefixes & 0x3) << 8);
#elif CFG_MMX
          UINT idx = i1 | ((prefixes & 0x1) << 8);
#else
          UINT idx = i1;
#endif
          func = x86Table2[idx].func[tmp.fOpSize];
          bits = x86Table2[idx].bits;
          ofs++;
        }
          goto after_initial_code;
        case X86_BITS_TABLE_FP:
          goto after_initial_code;
        case X86_BITS_PREFIX_LOCK:
          tmp.prefixes |= X86_DEC_PREFIX_LOCK;
          break;
        case X86_BITS_PREFIX_OPSIZE:
          tmp.fOpSize = !tmp.fOpSize;
          tmp.prefixes |= (X86_DEC_PREFIX_OPSIZE | X86_DEC_PREFIX_SSEBIT0);
          break;
        case X86_BITS_PREFIX_ADDRSIZE:
          tmp.prefixes |=  X86_DEC_PREFIX_ADDRSIZE;
          break;
        case X86_BITS_PREFIX_REP:
          tmp.prefixes |= (X86_DEC_PREFIX_REP | X86_DEC_PREFIX_SSEBIT1);
          break;
        case X86_BITS_PREFIX_REPNE:
          tmp.prefixes |= (X86_DEC_PREFIX_REPNE | X86_DEC_PREFIX_SSEBIT0 | X86_DEC_PREFIX_SSEBIT1);
          break;
        case X86_BITS_PREFIX_SEGCS:
          tmp.segOverride = CS;
          tmp.prefixes |= (X86_DEC_PREFIX_SEG | X86_DEC_PREFIX_HINT_NOT_TAKEN);
          break;
        case X86_BITS_PREFIX_SEGDS:
          tmp.segOverride = DS;
          tmp.prefixes |= (X86_DEC_PREFIX_SEG | X86_DEC_PREFIX_HINT_TAKEN);
          break;
        case X86_BITS_PREFIX_SEGES:
          tmp.segOverride = ES;
          tmp.prefixes |= X86_DEC_PREFIX_SEG;
          break;
        case X86_BITS_PREFIX_SEGFS:
          tmp.segOverride = FS;
          tmp.prefixes |= X86_DEC_PREFIX_SEG;
          break;
        case X86_BITS_PREFIX_SEGGS:
          tmp.segOverride = GS;
          tmp.prefixes |= X86_DEC_PREFIX_SEG;
          break;
        case X86_BITS_PREFIX_SEGSS:
          tmp.segOverride = SS;
          tmp.prefixes |= X86_DEC_PREFIX_SEG;
          break;
        default:
          INVALIDOP();
          break;
      }
      // If we get here, we just parsed a prefix and need to check a new byte.
      i0 = LOAD_VIRTUAL_BYTE_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
      bits = x86Table1[i0].bits;
      ofs++;
    }
    func = x86Table1[i0].func[tmp.fOpSize];

after_initial_code:
    // interpret Mod/RM field and SIB byte, if present
    if (bits & X86_BITS_MODRM) {
      // interpret Mod/RM fields
      UINT ModRM = LOAD_VIRTUAL_BYTE_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
      tmp.mod = ModRM & 0xC0;
      tmp.bits543 = (ModRM >> 3) & 0x7;
      tmp.rm = ModRM & 0x7;
      ofs++;

      if (tmp.mod != 0xC0) {
        ModRMEntry* tbl;
        IVAL regBase;
        IVAL regIndex;
        UINT scale;
        IVAL disp;
        // interpret effective address encoding
        if (tmp.fAddrSize) {
          // 32-bit ModRM
          if (tmp.rm == 0x4) {
            tmp.sib = LOAD_VIRTUAL_BYTE_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs++;
            SIBEntry* sibtbl = &SIBTable[tmp.sib];
            if (((tmp.sib & 0x7) == 0x5)) {
              tbl = &RM32Table[(tmp.mod >> 3) | 5];
              LOAD_REG_ID_DWORD(regBase,tbl->base);
              tmp.segOverride = (tmp.segOverride == SEG_NONE) ? tbl->seg : tmp.segOverride;
            } else {
              tbl = &RM32Table[(tmp.mod >> 3) | tmp.rm];
              LOAD_REG_ID_DWORD(regBase,sibtbl->base);
              tmp.segOverride = (tmp.segOverride == SEG_NONE) ? sibtbl->seg : tmp.segOverride;
            }
            LOAD_REG_ID_DWORD(regIndex,sibtbl->index);
            scale = sibtbl->scale;
            // note that the displacement is still provided by ModRM table
          } else {
            tbl = &RM32Table[(tmp.mod >> 3) | tmp.rm];
            LOAD_REG_ID_DWORD(regBase,tbl->base);
            LOAD_REG_ID_DWORD(regIndex,tbl->index);
            tmp.segOverride = (tmp.segOverride == SEG_NONE) ? tbl->seg : tmp.segOverride;
            scale = 1;
          }
        } else {
          // 16-bit ModRM
          tbl = &RM16Table[(tmp.mod >> 3) | tmp.rm];
          LOAD_REG_ID_WORD(regBase,tbl->base);
          LOAD_REG_ID_WORD(regIndex,tbl->index);
          tmp.segOverride = (tmp.segOverride == SEG_NONE) ? tbl->seg : tmp.segOverride;
          scale = 1;
        }

        // interpret displacement
        switch (tbl->disp) {
          case 0:
            disp = 0;
            break;
          case 1:
            disp = (IVAL)(INT8)LOAD_VIRTUAL_BYTE_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs += 1;
            break;
          case 2:
            disp = (IVAL)(INT16)LOAD_VIRTUAL_UNALIGNED_WORD_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs += 2;
            break;
          case 4:
            disp = (IVAL)(INT32)LOAD_VIRTUAL_UNALIGNED_DWORD_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs += 4;
            break;
          default:
            disp = 0;
            ASSERT(UNREACHED);
            break;
        }
        // TODO: is there 16-bit trunc in 16-bit addr mode?
        tmp.va = regBase + regIndex*scale + disp;

        // TODO: fix up long constants
        if (tmp.fAddrSize) {
          tmp.va = tmp.va & 0xFFFFFFFF;
        } else {
          tmp.va = tmp.va & 0xFFFF;
        }
      }

      // if op selection is based on ModRM, figure it out now
      if (bits & X86_BITS_GROUP) {
        UINT idx = bits & X86_BITS_GROUP_MASK;
        if ((bits & X86_BITS_EXTGROUP) && (tmp.mod == 0xC0)) {
          func = x86TableGroup[idx][tmp.bits543+8].func[tmp.fOpSize];
          bits = x86TableGroup[idx][tmp.bits543+8].bits;
        } else {
          func = x86TableGroup[idx][tmp.bits543].func[tmp.fOpSize];
          bits = x86TableGroup[idx][tmp.bits543].bits;
        }
      }
      else if (bits & X86_BITS_FP) {
#if CFG_FPU
        if (tmp.mod != 0xC0) {
          UINT idx = i0 - 0xD8;
          func  = x86TableFP[idx][tmp.bits543].func[0];
          bits = x86TableFP[idx][tmp.bits543].bits;
        } else {
          UINT idx = i0 - 0xD8 + 8;
          func  = x86TableFP[idx][ModRM & 0x3F].func[0];
          bits = x86TableFP[idx][ModRM & 0x3F].bits;          
        }
#else
        INVALIDOP();
#endif
      }
    } else {
      tmp.segOverride = (tmp.segOverride == SEG_NONE) ? DS : tmp.segOverride;
    }

    // note that at this point, X86_BITS_MODRM may not be set even if it had a ModRM earlier...
    // this is the case if bits543 causes another table lookup, for example
    // Consequently, the X86_BITS_IMM* bits must be set on the last table encoding that sets "bits".
    // Note that ModRM may cause table lookups but that simply decoding ModRM address does not set "bits".

    // If we still don't have the function at this point, it is invalid.
    // However, it is more efficient to have the processing call go to INVALIDOP handler
    // instead of having an explicit check here.
 
    // at this point, we can check prefixes for validity
    // TODO: but what is the exact right spot? wrt imms?

#ifdef WITH_PIN_SUPPORT
    PINSTRFUNC tmpFunc = NULL;
    if((tmpFunc = checkSSE(&ofs)) != NULL) {
        func = tmpFunc;
    } else
#endif // WITH_PIN_SUPPORT
    if (tmp.prefixes) {
      // LOCK prefix is only valid with specific opcodes and with a memory operand
      if ((tmp.prefixes & X86_DEC_PREFIX_LOCK) && 
          (!(bits & X86_BITS_LOCKABLE) || (tmp.mod == 0xC0)))
      {
        INVALIDOP();
      }
      // REP prefixes are only valid with specific opcodes.
      // Also can't have both types of REPs on one op
      // TODO: Are REP prefixes on non-string ops really faulting?
      // TODO: Or is it part of SSE extensions?
      if ((tmp.prefixes & (X86_DEC_PREFIX_REP | X86_DEC_PREFIX_REPNE)) &&
          (!(bits & X86_BITS_ALLOWREP) || 
          ((tmp.prefixes & (X86_DEC_PREFIX_REP | X86_DEC_PREFIX_REPNE)) == 
            (X86_DEC_PREFIX_REP | X86_DEC_PREFIX_REPNE))))
      {
        INVALIDOP();
      }
    }

    // check for immediate operands
    if (bits & X86_BITS_IMM0_MASK) {
      // decode first immediate (usually there is only one)
      switch (bits & X86_BITS_IMM0_MASK) {
        case X86_BITS_IMM8:
          tmp.imm0 = LOAD_VIRTUAL_BYTE_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
          ofs += 1;
          break;
        case X86_BITS_IMM16:
          tmp.imm0 = LOAD_VIRTUAL_UNALIGNED_WORD_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
          ofs += 2;
          break;
        case X86_BITS_IMMV:
          if (tmp.fOpSize) {
            tmp.imm0 = LOAD_VIRTUAL_UNALIGNED_DWORD_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs += 4;
          } else {
            tmp.imm0 = LOAD_VIRTUAL_UNALIGNED_WORD_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs += 2;
          }
          break;
        case X86_BITS_OFSV:
          if (tmp.fAddrSize) {
            tmp.imm0 = LOAD_VIRTUAL_UNALIGNED_DWORD_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs += 4;
          } else {
            tmp.imm0 = LOAD_VIRTUAL_UNALIGNED_WORD_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs += 2;
          }
          break;
        case X86_BITS_REL8:
          tmp.imm0 = (SVAL)(INT8)LOAD_VIRTUAL_BYTE_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
          ofs += 1;
          break;
        case X86_BITS_RELV:        
          if (tmp.fOpSize) {
            tmp.imm0 = (SVAL)(INT32)LOAD_VIRTUAL_UNALIGNED_DWORD_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs += 4;
          } else {
            tmp.imm0 = (SVAL)(INT16)LOAD_VIRTUAL_UNALIGNED_WORD_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs += 2;
          }
          break;
        default:
          ASSERT(UNREACHED);
          break;
      }

      // a few rare ops can have 2 immediates, or a two-field immediate
      if (bits & X86_BITS_IMM1_MASK) {
        switch (bits & X86_BITS_IMM1_MASK) {
          case X86_BITS_IMM8_2:
            tmp.imm1 = LOAD_VIRTUAL_BYTE_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs += 1;
            break;
          case X86_BITS_IMMSEG:
            tmp.imm1 = LOAD_VIRTUAL_UNALIGNED_WORD_UNCACHED(curIP+ofs,CS,ACCESS_EXECUTE);
            ofs += 2;
            break;
          default:
            ASSERT(UNREACHED);
            break;
        }
      }
    }

    tmp.newIP = curIP + ofs;

    if (P->cfg.print_master && 
        P->cfg.print_inst_on_fetch &&
        global_sim->cfg.stats_enabled) 
    {
        PrintInstruction(P->getInstructionTrace(),
                         SEV_INFO,
                         this,
                         P->cfg.print_registers,
                         P->cfg.print_symbols);
    }

    /* execute */
    instrs_executed = true;

    // memory operations may occur and we need to halt the instruction until
    // the op finishes. in order to cleanly halt w/o modifying Ben's x86 code,
    // we setjmp here and jump back on stall (instrs_executed will be false)
    //
    Checkpoint cp;
    takeCheckpoint(&cp);
    if (!setjmp(memoryJumpEnv)) {
        // perform operation by calling func
        ASSERT(func != NULL);
        func();
    } else {
        ASSERT(!instrs_executed);
    }

    if (instrs_executed) {
        if (P->cfg.print_master && 
            P->cfg.print_inst_on_commit &&
            global_sim->cfg.stats_enabled) 
        {
            PrintInstruction(P->getInstructionTrace(),
                             SEV_INFO,
                             this,
                             P->cfg.print_registers,
                             P->cfg.print_symbols);
        }
        // update IP
        InterpCtxt.IP = tmp.newIP;
    } else {
        // restore register checkpoint...
        restoreCheckpoint(&cp);
    }

    curPE = NULL;

    return instrs_executed;
}

void INVALIDOP () {
  // TODO: setjmp/longjmp handling of exceptional situations
#if (CFG_DEBUG)
  DbgOutputStr("Invalid Opcode detected, breaking...\n");
  DEBUG_BREAKPOINT;
#endif
  if (curPE) {
#ifdef WITH_PIN_SUPPORT
        // jw : the following code helps when there is a legimate opcode
        // but not implemented in the simulator since not all
        // unimplmented opcodes are with ASSERT(UNREACHED).
        curPE->PrintInstruction(curPE->P->getInstructionTrace(),
                                SEV_INFO,
                                curPE,
                                curPE->P->cfg.print_registers,
                                curPE->P->cfg.print_symbols);
#endif // WITH_PIN_SUPPORT    
      Log->e("%d:%lld:%.16llX Invalid Opcode, exiting.\n", 
             curPE->getID(), global_sim->getSimclock(), curPE->getPC());
  }
  ASSERT(UNREACHED);
}

void CPU::takeCheckpoint (Checkpoint *cp) {
    cp->InterpCtxt = InterpCtxt;
}

void CPU::restoreCheckpoint (Checkpoint *cp) {
    InterpCtxt = cp->InterpCtxt;
    tmp.newIP = InterpCtxt.IP;
}

void CPU::copyCheckpoint (Checkpoint* fromCp, Checkpoint* toCp) {
    toCp->InterpCtxt = fromCp->InterpCtxt;
}

void CPU::decrementPCInCheckpoint (Checkpoint *cp) {
    cp->InterpCtxt.IP -= 2;
}

void CPU::resumeAtCheckpoint (Checkpoint *cp) {
    InterpCtxt = cp->InterpCtxt;
    tmp.newIP = InterpCtxt.IP;
}

void CPU::resumeAfterCheckpoint (Checkpoint *cp) {
    InterpCtxt = cp->InterpCtxt;
    InterpCtxt.IP += 2;
    tmp.newIP = InterpCtxt.IP;
}

void CPU::revokeReservations (VA ea) {
    ASSERT(false/*NYI*/);
}

Result CPU::MemoryRead (uint64 *result,
                        Reg vAddr,
                        uint length,
                        uint signed_load)
{
    VA a = vAddr & ~3ULL;

    // jwc: additional handling of reading last bytes in address space
    /* process initial words */
    uint64 res = 0;
    int ofs = vAddr - a;
    Result accessRes;
    switch (length) {
    case 1: {
        uint32 tmp[1];
        if ((accessRes = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, memlock)) != success) {   
            return accessRes;
        }        
        res = (tmp[0] >> (ofs << 3));
        break;
    }
    case 2: {
        uint32 tmp[2];
        if (ofs < 3) {
            if ((accessRes = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, memlock)) != success) {   
                return accessRes;
            }
            res = tmp[0] >> (ofs << 3);
            break;
        }
        ASSERT(!memlock); // LL/SC should be aligned
        if ((accessRes = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, memlock)) != success) {   
            return accessRes;
        }
        if ((accessRes = P->sendWordAccess(a + 4, &tmp[1], WORDACCESS_LOAD, memlock)) != success) {   
            return accessRes;
        }        
        // have the machine do the load for you...
        res = *(uint16*)((uint)tmp + ofs);
        break;
    }
    case 4: {
        uint32 tmp[2];
        if (ofs == 0) {
            if ((accessRes = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, memlock)) != success) {   
                return accessRes;
            }            
            res = tmp[0];
            break;
        }
        ASSERT(!memlock); // LL/SC should be aligned
        if ((accessRes = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, memlock)) != success) {   
            return accessRes;
        }
        if ((accessRes = P->sendWordAccess(a + 4, &tmp[1], WORDACCESS_LOAD, memlock)) != success) {   
            return accessRes;
        }        
        // have the machine do the load for you...
        res = *(uint32*)((uint)tmp + ofs);

        break;
    }
    case 8: {
        ASSERT(!memlock); // don't support 64-bit LL/SC
        uint32 tmp[3];
        if (ofs == 0) {
            if ((accessRes = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, memlock)) != success) {   
                return accessRes;
            }
            res = tmp[0];
            a += 4;
            if ((accessRes = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, memlock)) != success) {   
                return accessRes;
            }
            res = (((UINT64)tmp[0]) << 32) | res;
        }
        else {
            if ((accessRes = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, memlock)) != success) {   
                return accessRes;
            }
            if ((accessRes = P->sendWordAccess(a + 4, &tmp[1], WORDACCESS_LOAD, memlock)) != success) {   
                return accessRes;
            }        
            if ((accessRes = P->sendWordAccess(a + 8, &tmp[2], WORDACCESS_LOAD, memlock)) != success) {   
                return accessRes;
            }        
            // have the machine do the load for you...
            res = *(uint64*)((uint)tmp + ofs);
        }
        break;
    }
    default: {
        ASSERT(false);
    }
    }

    /* handle sign-extension and zero-extension */
    // jwc: cutting off the wanted upper if the address is not 4 byte aligned
    switch ( (signed_load << 4) | length) {
      case 1: res = (uint64)(uint8)res; break;
      case 2: res = (uint64)(uint16)res; break;
      case 4: res = (uint64)(uint32)res; break;
      case 8: break;
      case 16+1: res = (int64)(int8)res; break;
      case 16+2: res = (int64)(int16)res; break;
      case 16+4: res = (int64)(int32)res; break;
      case 16+8: break;
      default:
                 ASSERT(false);
                 break;
    }

    *result = res;
    return success;
}

Result CPU::MemoryWrite (uint64 value, Reg vAddr, uint length) {
    VA a       = vAddr & ~3ULL;
    uint ofs   = vAddr & 3;
    uint64 val = value;

    // align data left within double-word
    uint shamt = ((8 - length) << 3);
    val = val << (uint64)shamt;

    // austenmc: so I cant seem to figure out how to change the ppc, big
    // endian code to i386, little endian code so I just wrote my own. its
    // not as pretty but it works...
    //
    // jac: If I need to load the word to store a sub-byte, I
    // shouldn't use LL if memlock=true, as this store is the SC to
    // some other LL.  For unaligned accesses you'll get two SC's,
    // which is weird.
    switch (length) {
    case 1: {
        uint32 tmp[1];
        uint hi_mask = partial_mask_hi[length][ofs];
        Result res = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, false);
        if (res != success) {
            return res;
        }
        tmp[0] = (tmp[0] & ~hi_mask) | ((val >> (32+((3-ofs) << 3))) & hi_mask);
        return P->sendWordAccess(a, &tmp[0], WORDACCESS_STORE, memlock);
    }
    case 2: {
        uint32 tmp[2];
        uint hi_mask = partial_mask_hi[length][ofs];
        Result res;
        if (ofs < 3) {
            Result res = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, false);
            if (res != success) {   
                return res;
            }
            tmp[0] = (tmp[0] & ~hi_mask) | ((val >> (32+((2-ofs) << 3))) & hi_mask);
            return P->sendWordAccess(a, &tmp[0], WORDACCESS_STORE, memlock);
        }
        ASSERT(!memlock); // shouldn't have unaligned LL/SC
        // we need both words...
        res = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, false);
        if (res != success) {   
            return res;
        }
        res = P->sendWordAccess(a + 4, &tmp[1], WORDACCESS_LOAD, false);
        if (res != success) {
            return res;
        }
        // have the machine do the store for you...
        *(uint16*)((uint)tmp + ofs) = (uint16)value;

        // write them back...
        res = P->sendWordAccess(a, &tmp[0], WORDACCESS_STORE, memlock);
        if (res != success) {   
            return res;
        }
        return P->sendWordAccess(a + 4, &tmp[1], WORDACCESS_STORE, memlock);
    }
    case 4: {
        uint32 tmp[2];
        if (ofs == 0) {
            tmp[0] = val >> 32;
            return P->sendWordAccess(a, &tmp[0], WORDACCESS_STORE, memlock);
        }
        ASSERT(!memlock); // shouldn't have unaligned LL/SC
        // we need both words...
        Result res = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, memlock);
        if (res != success) {   
            return res;
        }
        res = P->sendWordAccess(a + 4, &tmp[1], WORDACCESS_LOAD, memlock);
        if (res != success) {
            return res;
        }
        // have the machine do the store for you...
        *(uint32*)((uint)tmp + ofs) = (uint32)value;

        // write them back...
        res = P->sendWordAccess(a, &tmp[0], WORDACCESS_STORE, memlock);
        if (res != success) {   
            return res;
        }
        return P->sendWordAccess(a + 4, &tmp[1], WORDACCESS_STORE, memlock);
    }
    case 8: {
        ASSERT(!memlock); // don't support 64-bit LL/SC
        uint32 tmp[3];
        if (ofs == 0) {
            tmp[0] = val & 0x00000000ffffffffULL;
            Result res = P->sendWordAccess(a, &tmp[0], WORDACCESS_STORE, memlock);
            if (res != success) {   
                return res;
            }
            val = val >> 32;
            a += 4;
            tmp[0] = val & 0xffffffffULL;
            return P->sendWordAccess(a, &tmp[0], WORDACCESS_STORE, memlock);
        }
        // we need three words...
        Result res = P->sendWordAccess(a, &tmp[0], WORDACCESS_LOAD, memlock);
        if (res != success) {   
            return res;
        }
        res = P->sendWordAccess(a + 4, &tmp[1], WORDACCESS_LOAD, memlock);
        if (res != success) {
            return res;
        }
        res = P->sendWordAccess(a + 8, &tmp[2], WORDACCESS_LOAD, memlock);
        if (res != success) {
            return res;
        }
        // have the machine do the store for you...
        *(uint64*)((uint)tmp + ofs) = value;

        // write them back...
        res = P->sendWordAccess(a, &tmp[0], WORDACCESS_STORE, memlock);
        if (res != success) {   
            return res;
        }
        res = P->sendWordAccess(a + 4, &tmp[1], WORDACCESS_STORE, memlock);
        if (res != success) {   
            return res;
        }
        return P->sendWordAccess(a + 8, &tmp[2], WORDACCESS_STORE, memlock);
    }
    default: {
        ASSERT(false);
    }
    }
    ASSERT(false);
    return failure;
}

void CPU::printHeartbeat_extra () {
}

bool CPU::repInst () {
    // rep prefix is either 0xf2 or 0xf3 
    UINT32 addr = (UINT32)InterpCtxt.IP;
    return ((*((UINT8*)addr) & 0xFE) == 0xf2);
}

void CPU::goto_real () {
    InterpCtxt.IP += 2;  // skip this instruction
    // native execution hook
    DbgNativeExec();
    // when you reach here, it is because you have run goto_sim and have returned from DbgNativeExec...
}

IVAL CPU::getEFlags() {
    CPU* oldCurPE = curPE;
    curPE = this;
    IVAL eflags = getFlags();
    curPE = oldCurPE;
    return eflags;
}

void CPU::setEFlags(IVAL eflags) {
    CPU* oldCurPE = curPE;
    curPE = this;
    setFlags(eflags);
    curPE = oldCurPE;
}

/**
   Read a simulated register
*/
bool CPU::getRegister(int regnum, size_t dstLen, uint8 *dstData) {
    uint8* srcData;
    size_t srcLen;
    switch (regnum) {
    case REGNUM_eax:    srcData = (uint8*)&InterpCtxt.GPR[EAX];          srcLen = sizeof(InterpCtxt.GPR[EAX]);          break;
    case REGNUM_ecx:    srcData = (uint8*)&InterpCtxt.GPR[ECX];          srcLen = sizeof(InterpCtxt.GPR[ECX]);          break;
    case REGNUM_edx:    srcData = (uint8*)&InterpCtxt.GPR[EDX];          srcLen = sizeof(InterpCtxt.GPR[EDX]);          break;
    case REGNUM_ebx:    srcData = (uint8*)&InterpCtxt.GPR[EBX];          srcLen = sizeof(InterpCtxt.GPR[EBX]);          break;
    case REGNUM_esp:    srcData = (uint8*)&InterpCtxt.GPR[ESP];          srcLen = sizeof(InterpCtxt.GPR[ESP]);          break;
    case REGNUM_ebp:    srcData = (uint8*)&InterpCtxt.GPR[EBP];          srcLen = sizeof(InterpCtxt.GPR[EBP]);          break;
    case REGNUM_esi:    srcData = (uint8*)&InterpCtxt.GPR[ESI];          srcLen = sizeof(InterpCtxt.GPR[ESI]);          break;
    case REGNUM_edi:    srcData = (uint8*)&InterpCtxt.GPR[EDI];          srcLen = sizeof(InterpCtxt.GPR[EDI]);          break;
    case REGNUM_eip:    srcData = (uint8*)&InterpCtxt.IP;                srcLen = sizeof(InterpCtxt.IP);                break;
    case REGNUM_eflags: srcData = (uint8*)&InterpCtxt.eflags;            srcLen = sizeof(InterpCtxt.eflags);            break;
    case REGNUM_cs:     srcData = (uint8*)&InterpCtxt.SegTable[CS].base; srcLen = sizeof(InterpCtxt.SegTable[CS].base); break;
    case REGNUM_ss:     srcData = (uint8*)&InterpCtxt.SegTable[SS].base; srcLen = sizeof(InterpCtxt.SegTable[SS].base); break;
    case REGNUM_ds:     srcData = (uint8*)&InterpCtxt.SegTable[DS].base; srcLen = sizeof(InterpCtxt.SegTable[DS].base); break;
    case REGNUM_es:     srcData = (uint8*)&InterpCtxt.SegTable[ES].base; srcLen = sizeof(InterpCtxt.SegTable[ES].base); break;
    case REGNUM_fs:     srcData = (uint8*)&InterpCtxt.SegTable[FS].base; srcLen = sizeof(InterpCtxt.SegTable[FS].base); break;
    case REGNUM_gs:     srcData = (uint8*)&InterpCtxt.SegTable[GS].base; srcLen = sizeof(InterpCtxt.SegTable[GS].base); break;
    case REGNUM_st0:    srcData = (uint8*)&InterpCtxt.FPR[0];            srcLen = sizeof(InterpCtxt.FPR[0]);            break;
    case REGNUM_st1:    srcData = (uint8*)&InterpCtxt.FPR[1];            srcLen = sizeof(InterpCtxt.FPR[1]);            break;
    case REGNUM_st2:    srcData = (uint8*)&InterpCtxt.FPR[2];            srcLen = sizeof(InterpCtxt.FPR[2]);            break;
    case REGNUM_st3:    srcData = (uint8*)&InterpCtxt.FPR[3];            srcLen = sizeof(InterpCtxt.FPR[3]);            break;
    case REGNUM_st4:    srcData = (uint8*)&InterpCtxt.FPR[4];            srcLen = sizeof(InterpCtxt.FPR[4]);            break;
    case REGNUM_st5:    srcData = (uint8*)&InterpCtxt.FPR[5];            srcLen = sizeof(InterpCtxt.FPR[5]);            break;
    case REGNUM_st6:    srcData = (uint8*)&InterpCtxt.FPR[6];            srcLen = sizeof(InterpCtxt.FPR[6]);            break;
    case REGNUM_st7:    srcData = (uint8*)&InterpCtxt.FPR[7];            srcLen = sizeof(InterpCtxt.FPR[7]);            break;
    case REGNUM_fctrl:  srcData = (uint8*)&InterpCtxt.FPcontrol;         srcLen = sizeof(InterpCtxt.FPcontrol);         break;
    case REGNUM_fstat:  srcData = (uint8*)&InterpCtxt.FPstatus;          srcLen = sizeof(InterpCtxt.FPstatus);          break;
    case REGNUM_ftag:   srcData = (uint8*)&InterpCtxt.FPtag;             srcLen = sizeof(InterpCtxt.FPtag);             break;
    case REGNUM_fiseg:  memset(dstData, 0, dstLen); return true;
    case REGNUM_fioff:  memset(dstData, 0, dstLen); return true;
    case REGNUM_foseg:  memset(dstData, 0, dstLen); return true;
    case REGNUM_fooff:  memset(dstData, 0, dstLen); return true;
    case REGNUM_fop:    memset(dstData, 0, dstLen); return true;
    case REGNUM_xmm0:   memset(dstData, 0, dstLen); return true;
    case REGNUM_xmm1:   memset(dstData, 0, dstLen); return true;
    case REGNUM_xmm2:   memset(dstData, 0, dstLen); return true;
    case REGNUM_xmm3:   memset(dstData, 0, dstLen); return true;
    case REGNUM_xmm4:   memset(dstData, 0, dstLen); return true;
    case REGNUM_xmm5:   memset(dstData, 0, dstLen); return true;
    case REGNUM_xmm6:   memset(dstData, 0, dstLen); return true;
    case REGNUM_xmm7:   memset(dstData, 0, dstLen); return true;
    case REGNUM_mxcsr:  memset(dstData, 0, dstLen); return true;
    default:  
        /* provide 0 for undefined regs */
        memset(dstData, 0, dstLen); 
        return true;
    }
    ASSERT(srcLen >= dstLen);
    memcpy(dstData, srcData, dstLen);
    return true;
}

/**
   Write a simulated register
*/
bool CPU::putRegister(int regnum, size_t srcLen, uint8* srcData) {
    uint8* dstData;
    size_t dstLen;
    switch (regnum) {
    case REGNUM_eax:    dstData = (uint8*)&InterpCtxt.GPR[EAX];          dstLen = sizeof(InterpCtxt.GPR[EAX]);          break;
    case REGNUM_ecx:    dstData = (uint8*)&InterpCtxt.GPR[ECX];          dstLen = sizeof(InterpCtxt.GPR[ECX]);          break;
    case REGNUM_edx:    dstData = (uint8*)&InterpCtxt.GPR[EDX];          dstLen = sizeof(InterpCtxt.GPR[EDX]);          break;
    case REGNUM_ebx:    dstData = (uint8*)&InterpCtxt.GPR[EBX];          dstLen = sizeof(InterpCtxt.GPR[EBX]);          break;
    case REGNUM_esp:    dstData = (uint8*)&InterpCtxt.GPR[ESP];          dstLen = sizeof(InterpCtxt.GPR[ESP]);          break;
    case REGNUM_ebp:    dstData = (uint8*)&InterpCtxt.GPR[EBP];          dstLen = sizeof(InterpCtxt.GPR[EBP]);          break;
    case REGNUM_esi:    dstData = (uint8*)&InterpCtxt.GPR[ESI];          dstLen = sizeof(InterpCtxt.GPR[ESI]);          break;
    case REGNUM_edi:    dstData = (uint8*)&InterpCtxt.GPR[EDI];          dstLen = sizeof(InterpCtxt.GPR[EDI]);          break;
    case REGNUM_eip:    dstData = (uint8*)&InterpCtxt.IP;                dstLen = sizeof(InterpCtxt.IP);                break;
    case REGNUM_eflags: dstData = (uint8*)&InterpCtxt.eflags;            dstLen = sizeof(InterpCtxt.eflags);            break;
    case REGNUM_cs:     dstData = (uint8*)&InterpCtxt.SegTable[CS].base; dstLen = sizeof(InterpCtxt.SegTable[CS].base); break;
    case REGNUM_ss:     dstData = (uint8*)&InterpCtxt.SegTable[SS].base; dstLen = sizeof(InterpCtxt.SegTable[SS].base); break;
    case REGNUM_ds:     dstData = (uint8*)&InterpCtxt.SegTable[DS].base; dstLen = sizeof(InterpCtxt.SegTable[DS].base); break;
    case REGNUM_es:     dstData = (uint8*)&InterpCtxt.SegTable[ES].base; dstLen = sizeof(InterpCtxt.SegTable[ES].base); break;
    case REGNUM_fs:     dstData = (uint8*)&InterpCtxt.SegTable[FS].base; dstLen = sizeof(InterpCtxt.SegTable[FS].base); break;
    case REGNUM_gs:     dstData = (uint8*)&InterpCtxt.SegTable[GS].base; dstLen = sizeof(InterpCtxt.SegTable[GS].base); break;
    case REGNUM_st0:    dstData = (uint8*)&InterpCtxt.FPR[0];            dstLen = sizeof(InterpCtxt.FPR[0]);            break;
    case REGNUM_st1:    dstData = (uint8*)&InterpCtxt.FPR[1];            dstLen = sizeof(InterpCtxt.FPR[1]);            break;
    case REGNUM_st2:    dstData = (uint8*)&InterpCtxt.FPR[2];            dstLen = sizeof(InterpCtxt.FPR[2]);            break;
    case REGNUM_st3:    dstData = (uint8*)&InterpCtxt.FPR[3];            dstLen = sizeof(InterpCtxt.FPR[3]);            break;
    case REGNUM_st4:    dstData = (uint8*)&InterpCtxt.FPR[4];            dstLen = sizeof(InterpCtxt.FPR[4]);            break;
    case REGNUM_st5:    dstData = (uint8*)&InterpCtxt.FPR[5];            dstLen = sizeof(InterpCtxt.FPR[5]);            break;
    case REGNUM_st6:    dstData = (uint8*)&InterpCtxt.FPR[6];            dstLen = sizeof(InterpCtxt.FPR[6]);            break;
    case REGNUM_st7:    dstData = (uint8*)&InterpCtxt.FPR[7];            dstLen = sizeof(InterpCtxt.FPR[7]);            break;
    case REGNUM_fctrl:  dstData = (uint8*)&InterpCtxt.FPcontrol;         dstLen = sizeof(InterpCtxt.FPcontrol);         break;
    case REGNUM_fstat:  dstData = (uint8*)&InterpCtxt.FPstatus;          dstLen = sizeof(InterpCtxt.FPstatus);          break;
    case REGNUM_ftag:   dstData = (uint8*)&InterpCtxt.FPtag;             dstLen = sizeof(InterpCtxt.FPtag);             break;
    case REGNUM_fiseg: return true;
    case REGNUM_fioff: return true;
    case REGNUM_foseg: return true;
    case REGNUM_fooff: return true;
    case REGNUM_fop:   return true;
    case REGNUM_xmm0:  return true;
    case REGNUM_xmm1:  return true;
    case REGNUM_xmm2:  return true;
    case REGNUM_xmm3:  return true;
    case REGNUM_xmm4:  return true;
    case REGNUM_xmm5:  return true;
    case REGNUM_xmm6:  return true;
    case REGNUM_xmm7:  return true;
    case REGNUM_mxcsr: return true;
    default:
        /* Ignore undefined regs */
        return true;
    }
    ASSERT(dstLen >= srcLen);
    memcpy(dstData, srcData, srcLen);
    memset(dstData+srcLen, 0, dstLen-srcLen);
    return true;
}


#ifdef WITH_PIN_SUPPORT
// jw : we don't have SSE support at the moment.
// the following two instructions are special support to run Intel PIN.
// movq (%ecx), %xmm0
// movq %xmm0, (%ecx)
// a proper way to have the decoding code is to add new decoding tables.
// i tried it but ended up adding a bunch of tables just to say they are all invalid.
// the code below is much cleaner for the special support.
// TODO: this should be revamped when we decide to support SSE fully.
PINSTRFUNC CPU::checkSSE(UINT* ofs) {
    UINT32 addr = (uint)InterpCtxt.IP;

    if(*((UINT8*)addr) == 0xf3 && *((UINT8*)addr + 1) == 0x0f && *((UINT8*)addr + 2) == 0x7e) 
    {
        // movq (%ecx), %xmm0
        (*ofs)++;
        return MOVQ_ECX_XMM;
    } else if(*((UINT8*)addr) == 0x66 && *((UINT8*)addr + 1) == 0x0f && *((UINT8*)addr + 2) == 0xd6)
    {
        // movq %xmm0, (%ecx)
        (*ofs)++;
        return MOVQ_XMM_ECX;
    } else if(*((UINT8*)addr) == 0xf3 && *((UINT8*)addr + 1) == 0x0f && *((UINT8*)addr + 2) == 0x7f) 
    {
        // movdqu %xmm0,IMM
        (*ofs)+=4;
        return MOVQ_XMM_IMM;
    }
    
    return NULL;
}
#endif // WITH_PIN_SUPPORT

